package com.utils

import com.entity.PackPath
import com.entity.yaml.AppConfig
import com.entity.yaml.Languages
import com.entity.yaml.Project
import extend.isAllNumeric
import org.apache.logging.log4j.kotlin.logger
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean
import org.springframework.boot.context.properties.bind.Bindable
import org.springframework.boot.context.properties.bind.Binder
import org.springframework.core.env.PropertiesPropertySource
import org.springframework.core.env.StandardEnvironment
import org.springframework.core.io.FileSystemResource
import kotlin.reflect.KClass

/**
 * 配置
 */
object YamlUtil {

    /**
     * 初始化一个标准环境。
     * 通过 lazy 修饰符保证该环境只在首次访问时被初始化。
     */
    val environment by lazy {
        StandardEnvironment()
    }

    /**
     * 用于缓存 YAML 配置的 Map。
     */
    val yamlCache = mutableMapOf<String, Any>()

    var lastLoaderPath: PackPath? = null

    /**
     * 加载指定路径的 YAML 配置文件。
     *
     * @param path 配置文件的路径，可以是文件系统中的绝对或相对路径。
     */
    fun loadYaml(path: String) {
        loadYaml(PackPath(path))
    }

    /**
     * 加载指定 PackPath 对象所表示的 YAML 配置文件。
     * 如果文件不存在，则不执行任何操作。
     *
     * @param path 配置文件的路径对象。
     */
    fun loadYaml(path: PackPath) {
        if (path.notExists()) {
            logger.warn("加载 yaml 配置文件不存在：${path.absolutePathString}")
            return
        }
        this.lastLoaderPath = path
        logger.info("加载 yaml 配置文件：${path.absolutePathString}")
        // 设置 YAML 配置工厂，关联指定的文件路径
        val factory = YamlPropertiesFactoryBean()
        factory.setResources(FileSystemResource(path.toPath))
        // 加载并转换 YAML 文件为属性集合
        val properties = factory.`object`!!
        if (unpackEnvName != null) {
            properties["env"] = unpackEnvName
        }

        properties.replaceAll { _, u ->
            var value = u
            if (u is String) {
                value = u.replace("\\\\", "/")
                value = value.replace("\\", "/")
            }
//            println("$t $u $value")
            value
        }

        // 创建一个新的属性源，将 YAML 配置加入环境变量中
        val propertySource = PropertiesPropertySource(path.nameWithoutExtension, properties)
        environment.propertySources.addLast(propertySource)
    }

    /**
     * 获取AppConfig。
     */
    fun getAppConfig(): AppConfig {
        return getConfig("config", AppConfig::class) {
            val projects = it.projects
            // 将项目目录映射为Project对象并添加到项目列表中
            val project = it.projectDir.map { project ->
                Project(project)
            }
            projects.addAll(project)

            // 构建基础路径列表，用于后续路径解析
            val baseList = arrayOf(lastLoaderPath?.parent?.absolutePathString, System.getProperty("user.dir")).filterNotNull()
            // 为每个项目输入解析并设置输入路径
            projects.forEach { input ->
                var packPath: PackPath? = null
                for (base in baseList) {
                    val path = PackPath.format(input.inputFile, base)
                    if (path.exists()) {
                        packPath = path
                        break
                    }
                }
                input.inputPath = packPath
            }

            it
        }!!
    }

    fun getLanguages(): Languages? {
        return getConfig("languages", Languages::class)
    }

    /**
     * 通过配置名称和类型获取配置项。
     *
     * @param name 配置项的名称。
     * @param kClass 配置项的类型。
     * @param onCreate 可选的配置对象创建回调函数，用于在创建配置对象后进行额外处理
     * @return 返回配置项的值，如果不存在或类型不匹配则返回 null。
     */
    inline fun <reified T : Any> getConfig(
        name: String,
        kClass: KClass<T>,
        noinline onCreate: ((t: T) -> T)? = null
    ): T? {
        // 尝试从缓存中获取配置项，如果不存在或类型不匹配，则重新绑定'
        val configName = name.lowercase()
        var config = yamlCache[configName]
        runCatching {
            if (config == null || config !is T) {
                config = Binder.get(environment)
                    .bind(configName, Bindable.of(kClass.java))
                    .get()
                config = onCreate?.invoke(config) ?: config
                yamlCache[configName] = config as T
            }
        }.onFailure {
            logger.error("配置解析错误", it)
        }
        return config as? T
    }


    /**
     * 打包环境Id 1开始
     */
    var unpackEnvId: Int = -1

    /**
     * 打包环境 1开始
     */
    @Deprecated("use YamlUtil.unpackEnvId")
    var unpackScene: Int
        set(value) {
            unpackEnvId = value
        }
        get() = unpackEnvId

    /**
     * 打包环境 名字
     */
    var unpackEnvName: String? = null

    /**                提交方式                     */
    /** git提交内容  */
    var gitContent: String = "debug"

    /**
     * 打包环境映射
     * @param value 值
     * @return
     */
    fun getTypeMap(value: String): Int {
        var cmd = -1
        if (value.isAllNumeric()) {
            cmd = value.toInt()
        } else {
            val a = getAppConfig().runEnv
                .indexOfFirst { it.name == value }
            if (a == -1) error("执行环境不存在")
            cmd = a + 1
        }
        return cmd
    }

    /**
     * 打包环境映射
     * @param value 值
     * @return
     */
    fun getProjectScriptPathMap(value: String): String {
        var cmd = value
        if (!value.contains("/") && !value.contains("\\")) {
            cmd = ScriptUtils.scriptEngine
                .getVarValue<Map<String, String>>("projectScriptPath")?.get(value)!!
        }
        return cmd
    }

}
